package io.github.junxworks.qt.modules.mm.engine;

import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.ResultSetMetaData;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

import javax.sql.DataSource;

import org.stringtemplate.v4.ST;

import com.google.common.collect.Maps;

import io.github.junxworks.junx.core.exception.FatalException;
import io.github.junxworks.junx.core.lang.Initializable;
import io.github.junxworks.junx.core.util.StringUtils;
import io.github.junxworks.qt.constants.QtConstants;
import io.github.junxworks.qt.modules.mm.vo.FieldVo;
import io.github.junxworks.qt.modules.mm.vo.MmDatasourceVo;

/**
 * 数据流对象
 */
public class Datastream implements Initializable {

	private String dsName;

	private String sqlTemplate;

	private MmDatasourceVo datasource;

	private Map<String, StreamDataBundle> objCache = Maps.newHashMap();

	private List<FieldVo> fields;

	private Map<String, FieldVo> fieldMap;

	private ExeContext ctx;

	private String sql;

	private DataSource dataSource;

	private String keyColumn;

	public String getKeyColumn() {
		return keyColumn;
	}

	public void setKeyColumn(String keyColumn) {
		this.keyColumn = keyColumn;
	}

	public List<FieldVo> getFields() {
		return fields;
	}

	public FieldVo getFieldModel(String fieldName) {
		return fieldMap.get(fieldName);
	}

	public void setFields(List<FieldVo> fields) {
		this.fields = fields;
		fieldMap = fields.stream().collect(Collectors.toMap(FieldVo::getInnerEName, v -> v));
	}

	public DataSource getDataSource() {
		return dataSource;
	}

	public void setDataSource(DataSource dataSource) {
		this.dataSource = dataSource;
	}

	public Map<String, StreamDataBundle> getObjCache() {
		return objCache;
	}

	public String getDsName() {
		return dsName;
	}

	public void setDsName(String dsName) {
		this.dsName = dsName;
	}

	public ExeContext getCtx() {
		return ctx;
	}

	public void setCtx(ExeContext ctx) {
		this.ctx = ctx;
	}

	public String getSqlTemplate() {
		return sqlTemplate;
	}

	public void setSqlTemplate(String sqlTemplate) {
		this.sqlTemplate = sqlTemplate;
	}

	public MmDatasourceVo getDatasource() {
		return datasource;
	}

	public void setDatasource(MmDatasourceVo datasource) {
		this.datasource = datasource;
	}

	public void extractData(DataContext dc) {
		String key = dc.getKey().getValue();
		if (objCache.containsKey(key)) {
			//数据流已经执行完了，没数据了，最后一个数据便是key对应数据
			dc.addData(objCache.remove(key));
		} else {
			dc.addData(null);
		}
	}

	@Override
	public void initialize() throws Exception {
		//初始化执行sql
		if (StringUtils.isNull(sqlTemplate)) {
			throw new FatalException("Null sql template.");
		}
		ST st = new ST(sqlTemplate, '{', '}');
		if (StringUtils.notNull(ctx.getTsCodesCondition())) {
			st.add("tsCodes", ctx.getTsCodesCondition());
		}
		st.add("tradeDate", ctx.getTradeDate());
		this.sql = st.render();
		try (Connection conn = dataSource.getConnection(); PreparedStatement ps = conn.prepareStatement(sql, ResultSet.TYPE_FORWARD_ONLY, ResultSet.CONCUR_READ_ONLY);) {
			ps.setFetchSize(Integer.MAX_VALUE);
			try (ResultSet rs = ps.executeQuery()) {
				ResultSetMetaData md = rs.getMetaData();
				int columnCount = md.getColumnCount();
				while (rs.next()) {
					Map<String, Object> row = Maps.newHashMap();
					for (int i = 1; i <= columnCount; i++) {
						String columnName = md.getColumnName(i);
						row.put(columnName, rs.getObject(i));
					}
					String rowKey = String.valueOf(row.get(keyColumn));//数据库中的key
					if (QtConstants.KEYWORD_TS_CODE.equals(keyColumn) && ctx.isAbandoned(rowKey)) {
						continue;
					}
					StreamDataBundle bundle = objCache.get(rowKey);
					if (bundle == null) {
						bundle = new StreamDataBundle();
						bundle.setKey(rowKey);
						bundle.addRow(row);
						bundle.setDataStreamName(dsName);
						objCache.put(rowKey, bundle);
					} else {
						bundle.addRow(row);
					}
				}
			}
		}
	}

	@Override
	public void destroy() throws Exception {
		this.objCache.clear();
	}

}
